package com.senyint.hip.fhir.provider;

import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.Delete;
import ca.uhn.fhir.rest.annotation.History;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.OptionalParam;
import ca.uhn.fhir.rest.annotation.Read;
 
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
 

 
import org.apache.commons.lang3.StringUtils;
 
import org.hl7.fhir.dstu3.model.CodeableConcept;
import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.dstu3.model.IdType;
 
import org.hl7.fhir.dstu3.model.OperationOutcome;
 
import org.hl7.fhir.dstu3.model.ProcedureRequest;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.util.*;

/**
 * *******description*******
 * 检查申请
 * *************************
 *
 * @author ChenPan
 * @date 2018/10/29 10:50
 */
 
public class ProcedureRequestProvider implements IResourceProvider {

    /**
     * 临时存储数据
     */
    private Map<Long, Deque<ProcedureRequest>> prRes = new HashMap<Long, Deque<ProcedureRequest>>();

    /**
     * This is used to generate new IDs
     */
    private long myNextId = 1;
    
    @Override
    public Class<? extends IBaseResource> getResourceType() {
        return ProcedureRequest.class;
    }


    @Search
    public List<ProcedureRequest> findPrsUsingArbitraryCtriteria() {
        LinkedList<ProcedureRequest> retVal = new LinkedList<ProcedureRequest>();

        for (Deque<ProcedureRequest> nextPrList : prRes.values()) {
            ProcedureRequest nextPr = nextPrList.getLast();
            retVal.add(nextPr);
        }

        return retVal;
    }
    
    
	@Search()
	public List<ProcedureRequest> findProcedureRequestByCheckCode(@OptionalParam(name = "itemCode") String itemCode,
			@OptionalParam(name = "itemName") String itemName,@OptionalParam(name = "_id") String id ) {
		LinkedList<ProcedureRequest> retVal = new LinkedList<ProcedureRequest>();
		
		if(StringUtils.isEmpty(itemCode)&&StringUtils.isEmpty(itemName)&&StringUtils.isEmpty(id)) {
			return findPrsUsingArbitraryCtriteria();
		}
		

		for (Deque<ProcedureRequest> nextPrList : prRes.values()) {
			ProcedureRequest nextProcedureRequest = nextPrList.getLast();
			Long idPartAsLong = nextProcedureRequest.getIdElement().getIdPartAsLong();
			CodeableConcept code = nextProcedureRequest.getCode();

			List<Coding> codingList = code.getCoding();

			for (Coding coding : codingList) {
				String checkCode = coding.getCode();
				String checkName = coding.getDisplay();
				if (StringUtils.isNotBlank(itemCode)) {
					if (StringUtils.isNotBlank(itemName)) {
                          if(StringUtils.isNotBlank(id)) {
                        	  if (itemName.equals(checkName) && itemCode.equals(checkCode) && id.equals(idPartAsLong.toString())) {
        							retVal.add(nextProcedureRequest);

        						} 
                          }else {
                        	  if (itemName.equals(checkName) && itemCode.equals(checkCode)) {
      							retVal.add(nextProcedureRequest);

      						} 
                          }
						

					} else {

						if (itemCode.equals(checkCode)) {
							retVal.add(nextProcedureRequest);

						}

					}

				} else if (StringUtils.isNotBlank(itemName)) {
					if (itemName.equals(checkName)) {
						retVal.add(nextProcedureRequest);

					}

				}else if(StringUtils.isNotBlank(id)) {
					if (id.equals(idPartAsLong.toString())) {
						retVal.add(nextProcedureRequest);

					}
				}

			}

		}

		return retVal;
	}

    
    @Read(version = true)
    public ProcedureRequest readPatient(@IdParam IdType theId) {
        Deque<ProcedureRequest> retVal;
        try {
            retVal = prRes.get(theId.getIdPartAsLong());
            if(ObjectUtils.isEmpty(retVal)) {
            	  throw new ResourceNotFoundException("Unknown version: " + theId.getValue());
            }
        } catch (NumberFormatException e) {
            /*
             * If we can't parse the ID as a long, it's not valid so this is an unknown resource
             */
            throw new ResourceNotFoundException(theId);
        }

        if (theId.hasVersionIdPart() == false) {
            return retVal.getLast();
        } else {
            for (ProcedureRequest nextVersion : retVal) {
                String nextVersionId = nextVersion.getIdElement().getVersionIdPart();
                if (theId.getVersionIdPart().equals(nextVersionId)) {
                    return nextVersion;
                }
            }
            // No matching version
            throw new ResourceNotFoundException("Unknown version: " + theId.getValue());
        }

    }
    @History()
    public Deque<ProcedureRequest> historyProcedureRequest(@IdParam IdType theId) {
        Deque<ProcedureRequest> retVal;
        try {
            retVal = prRes.get(theId.getIdPartAsLong());
        } catch (NumberFormatException e) {
            /*
             * If we can't parse the ID as a long, it's not valid so this is an unknown resource
             */
            throw new ResourceNotFoundException(theId);
        }
        return retVal;
    }
   
    @Create()
    public MethodOutcome createProcedureRequest(@ResourceParam ProcedureRequest theProcedureRequest) {
       

        // Here we are just generating IDs sequentially
        long id = myNextId++;

        String versionId = addNewVersion(theProcedureRequest, id);

        MethodOutcome retVal = new MethodOutcome();
        retVal.setId(new IdType("ProcedureRequest", String.valueOf(id), versionId));

        OperationOutcome outcome = new OperationOutcome();
        outcome.setIdElement(new IdType("ProcedureRequest", String.valueOf(id), versionId));
        outcome.addIssue().setDiagnostics("新增检查申请单成功").setId(String.valueOf(id));
        retVal.setOperationOutcome(outcome);

        return retVal;
    }

    @Update()
    public MethodOutcome updateProcedureRequest(@IdParam IdType theId, @ResourceParam ProcedureRequest theProcedureRequest) {
       

        Long id;
        try {
            id = theId.getIdPartAsLong();
        } catch (DataFormatException e) {
            throw new InvalidRequestException("Invalid ID " + theId.getValue() + " - Must be numeric");
        }

        /*
         * Throw an exception (HTTP 404) if the ID is not known
         */
        if (!prRes.containsKey(id)) {
            throw new ResourceNotFoundException(theId);
        }

        String versionId = addNewVersion(theProcedureRequest, id);

        MethodOutcome retVal = new MethodOutcome();
        retVal.setId(new IdType("ProcedureRequest", String.valueOf(id), versionId));
        OperationOutcome outcome = new OperationOutcome();
        outcome.setIdElement(new IdType("ProcedureRequest", String.valueOf(id), versionId));
        outcome.addIssue().setDiagnostics("更新检查申请单成功").setId(String.valueOf(id));
        retVal.setOperationOutcome(outcome);

        return retVal;
    }
    

    @Delete
    public MethodOutcome deleteProcedureRequest(@IdParam IdType theId) {
        Long id;
        try {
            id = theId.getIdPartAsLong();
        } catch (DataFormatException e) {
            throw new InvalidRequestException("Invalid ID " + theId.getValue() + " - Must be numeric");
        }
        /*
         * Throw an exception (HTTP 404) if the ID is not known
         */
        if (!prRes.containsKey(id)) {
            throw new ResourceNotFoundException(theId);
        }

        prRes.remove(id);

        MethodOutcome retVal = new MethodOutcome();
        OperationOutcome outcome = new OperationOutcome();
        outcome.addIssue().setDiagnostics("删除检查申请单成功").setId(String.valueOf(id));
        retVal.setOperationOutcome(outcome);

        return retVal;
    }
    
    private String addNewVersion(ProcedureRequest theProcedureRequest, Long theId) {
        Date publishedDate;
        if (!prRes.containsKey(theId)) {
            prRes.put(theId, new LinkedList<ProcedureRequest>());
            publishedDate = InstantDt.withCurrentTime().getValue();
        } else {
        	ProcedureRequest currentProcedureRequest = prRes.get(theId).getLast();
            publishedDate = currentProcedureRequest.getMeta().getLastUpdated();
        }

        /*
         * PUBLISHED time will always be set to the time that the first version was stored. UPDATED time is set to the time that the new version was stored.
         */
        theProcedureRequest.getMeta().setLastUpdated(publishedDate);
        theProcedureRequest.getMeta().setLastUpdated(InstantDt.withCurrentTime().getValue());
        Deque<ProcedureRequest> existingVersions = prRes.get(theId);

        // We just use the current number of versions as the next version number
        String newVersion = Integer.toString(existingVersions.size());

        // Create an ID with the new version and assign it back to the resource
        IdType newId = new IdType("ProcedureRequest", Long.toString(theId), newVersion);
        theProcedureRequest.setId(newId);

        existingVersions.add(theProcedureRequest);
        return newVersion;
    }

}
